#include "icmp.h"

#include <winsock2.h>  // This must be included before "windows.h"
#include <windows.h>

#include "global.h"
#include "checksum.h"
#include "lists.h"

#include "ip.h"

void icmp4to6( eth_header *eh, ip_header *ih, icmp_header *icmph )
{
	int length = sizeof(eth_header) + ntohs(ih->length) + 20;  // Send packet length
	eth_header   *eh_6;
	ip6_header   *ip6h;
	icmp6_header *icmp6h;

	if( icmph->type == ICMP_ECHO || icmph->type == ICMP_ECHO_REPLY ) // Echo/Echo Reply Request
	{
		memset( v4SendBuffer, 0, 1600 );
		eh_6   = (eth_header   *)( v4SendBuffer );
		ip6h   = (ip6_header   *)( v4SendBuffer + sizeof(eth_header) );
		icmp6h = (icmp6_header *)( v4SendBuffer + sizeof(eth_header) + sizeof(ip6_header) );

		// Build Ethernet header
		int i;
		// Copy dst MAC
		for( i =0; i < 6; i++ )
			eh_6->dmac[i] = eh->dmac[i];
		// Fill src MAC with v6LocalMAC
		for( i =0; i < 6; i++ )
			eh_6->smac[i] = v6LocalMAC[i];
		eh_6->type = htons(ETH_IP6);

		// Build IPv6 header
		ip4to6( ih, ip6h );
		ip6h->nexthdr = IP_ICMP6;

		// Build ICMPv6 header
		icmp6h->type = ( (icmph->type == ICMP_ECHO) ? ICMP6_ECHO : ICMP6_ECHO_REPLY );
		icmp6h->code = 0;
		icmp6h->checksum = 0;
		icmp6h->id = icmph->id;
		icmp6h->sequence = icmph->sequence;

		// Add id to mapping lists
		icmp_id4to6_list[icmph->id].id_6 = icmp6h->id;
		icmp_id6to4_list[icmp6h->id].id_4 = icmph->id;
		icmp_id6to4_list[icmp6h->id].trans = 1;
		icmp6h->id = icmph->id;

		// Copy data
		const u_char *data = v4RecvBuffer + sizeof(eth_header) + sizeof(ip_header) + sizeof(icmp_header);
		u_char *data_new   = v4SendBuffer + sizeof(eth_header) + sizeof(ip6_header) + sizeof(icmp6_header);
		int data_size      = ntohs(ih->length) - (ih->ver_ihl & 0x0f) * 4 - sizeof(icmp_header);
		memcpy( data_new, data, data_size );

		checksum_icmp6( ip6h, icmp6h );

		// Done

		/* Send down the packet */
		if (pcap_sendpacket(v6Handle, v4SendBuffer, length /* size */) != 0)
		{
			printf("v4 device: Error sending the packet to v6 side: %s\n", pcap_geterr(v6Handle));
		}
	}

	return;

}

void icmp6to4( eth_header *eh, ip6_header *ip6h, icmp6_header *icmp6h )
{
	int length = sizeof(eth_header) + ntohs(ip6h->payload) + 20;  // Send packet length
	eth_header  *eh_4;
	ip_header   *ih;
	icmp_header *icmph;

	if( icmp6h->type == ICMP6_ECHO || icmp6h->type == ICMP6_ECHO_REPLY ) // Echo/Echo Reply Request
	{
		// Check the mapping list
		if( icmp_id6to4_list[icmp6h->id].trans != 1 )
			return;

		memset( v6SendBuffer, 0, 1600 );
		eh_4   = (eth_header  *)( v6SendBuffer );
		ih     = (ip_header   *)( v6SendBuffer + sizeof(eth_header) );
		icmph  = (icmp_header *)( v6SendBuffer + sizeof(eth_header) + sizeof(ip_header) );

		// Build Ethernet header
		int i;
		// Fill dst MAC with v4LocalMAC
		for( i =0; i < 6; i++ )
			eh_4->dmac[i] = v4LocalMAC[i];
		// Copy src MAC 
		for( i =0; i < 6; i++ )
			eh_4->smac[i] = eh->smac[i];
		eh_4->type = htons(ETH_IP);

		// Build IPv4 header
		ip6to4( ip6h, ih );
		ih->protocol = IP_ICMP;

		// Build ICMPv6 header
		icmph->type = ( (icmp6h->type == ICMP6_ECHO) ? ICMP_ECHO : ICMP_ECHO_REPLY );
		icmph->code = 0;
		icmph->checksum = 0;
		icmph->id = icmp_id6to4_list[icmp6h->id].id_4; // ! Bug here, need to be fixed in future
		icmph->sequence = icmp6h->sequence;

		// Copy data
		const u_char *data = v6RecvBuffer + sizeof(eth_header) + sizeof(ip6_header) + sizeof(icmp6_header);
		u_char *data_new   = v6SendBuffer + sizeof(eth_header) + sizeof(ip_header) + sizeof(icmp_header);
		int data_size      = ntohs(ip6h->payload) - sizeof(icmp6_header);
		memcpy( data_new, data, data_size );

		checksum_icmp4( ih, icmph );

		// Done

		/* Send down the packet */
		if (pcap_sendpacket(v4Handle, v6SendBuffer, length /* size */) != 0)
		{
			printf("v6 device: Error sending the packet to v4 side: %s\n", pcap_geterr(v4Handle));
		}
	}

	return;
}
